【算法】逆序对问题的四种解法(归并排序,BST,树状数组,线段树)及变形

本文详细介绍了四种解决数组中逆序对个数的方法:归并排序、二叉搜索树(BST)、树状数组和线段树。针对LeetCode的315题,通过这些方法分析如何找到元素后面比它小的元素个数,并探讨了逆序对问题的变形和不同解法的适用场景。
摘要由CSDN通过智能技术生成

引发我对逆序对这个问题思考的源自这道题:315. Count of Smaller Numbers After Self这道题要找出某个元素之后出现的比这个元素小的元素个数:ex. Given[5, 2, 6, 1], return [2, 1, 1, 0]

这道题只是求逆序对个数的另外一个变形而已,所以这篇就着重说一下数组中逆序对个数的四种求法。

归并方法

以前我以为求一个序列逆序对个数的解法就是归并排序的改版。归并解法:
因为要求每个元素的逆序对个数,所以一下解法的相当一部分花在来找原数组的对应关系上面。

vector<int> res;
void mergesort(vector<II>& nums,int left,int right){
    if(left>= right)
        return;
    int mid = (left+right)/2;
    mergesort(nums,left,mid);
    mergesort(nums,mid+1,right);
    vector<II> leftnums(mid-left+1),rightnums(right-mid);

    for(int i = left;i<=mid;i++)
        leftnums[i-left] = nums[i];
    for(int i = mid+1;i<=right;i++)
        rightnums[i-mid-1] = nums[i];
    leftnums.push_back(II(INT_MAX,-1));
    rightnums.push_back(II(INT_MAX,-1));
    int i = 0,j=0,k = left;
    int mark = 0;
    while(i<leftnums.size()-1||j<rightnums.size()-1){
        if(leftnums[i]<=rightnums[j]){
            res[leftnums[i].second] += mark;
            nums[k++] = leftnums[i++];
        }else{
            mark++;
            nums[k++] = rightnums[j++];
        }
    }
    return;
}
vector<int> countSmaller(vector<int>& nums) {
    res = vector<int>(nums.size(),0);
    vector<II> new_nums(nums.size());
    for(int i = 0;i<nums.size();i++)
        new_nums[i] = II(nums[i],i);
    mergesort(new_nums,0,nums.size()-1);
    return res;
}

BST(二叉搜索树)

先说一个额外的变形题型:

《程序员面试金典》上面的题目:P77 11.8题:读取一串整数流,要求尽可能短的时间内,返回已读取的数字流中,比x小的元素个数。设计
void add(int x)和int getRankOfNumber(int x)两个函数接口。

如果不是一个数字流的话,就直接将这个数组排序,然后每个元素排名出现的位置,就是整个数组中比其小的元素个数。
但是如果处在流中,则每次排序花费太大,此时需要一个数组结构能够完成这个工作。所以此时就想到二叉树是一个非常好的数据结构。【一般需要用到数据结构的问题中,就想一下:堆,二叉树,栈,队列这四个方面想就八九不离十了】
所以弄一个BST,每个节点中额外记录一下,这个节点左子树的元素个数LeftNum。

getRankOfNumber(int x)函数
就沿着BST数去查找这个元素所在的数节点,在查找的途中,如果要往左子树查找的话,就不做改变,如果要往右子树查找的话,即当前节点和其左子树所有节点都比它小,所以res
+= LeftNum+1


add(int x)函数
就是一个经典的BST插入,不过当其要插入当前节点的左子树节点中时,就将当前节点的LeftNum值加一即可。

再回到这道题本身,其实当时第一眼看到这道题的时候,我并没有往逆序对这种情况上去想,而是直接就参照上面那题的思路用二叉树去做了,二叉树的解法就是,从右往左一次扫描这个二叉树,进行建树,在插入节点的时候,就直接找出当前情况下它的rank值存到数组中即可。
我的代码如下:

struct TreeNode {
    int val;
    int count;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x,int c):val(x),count(c),left(NULL), right(NULL) {}
};
vector<int> countSmaller(vector<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值